package com.lehecai.core.search.api;

import com.lehecai.core.exception.ApiRemoteCallFailedException;
import com.lehecai.core.search.api.impl.AbstractSearchApiResponse;
import com.lehecai.core.search.api.impl.parser.ISearchApiResponseBodyParser;
import com.lehecai.core.search.api.impl.parser.SearchApiResponseParserBinder;
import com.lehecai.core.util.CharsetConstant;
import com.lehecai.core.util.CoreJSONUtils;
import com.lehecai.core.util.CoreStringUtils;
import com.lehecai.core.util.SocketConnectionFactory;
import com.lehecai.core.warning.IWarningTool;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Sunshow
 *
 */
public class SearchApiRequestService {
	
	protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private Map<SearchApiConfigItem, GenericObjectPool<Socket>> socketConnectionPool = new ConcurrentHashMap<SearchApiConfigItem, GenericObjectPool<Socket>>();
	
	private IWarningTool warningTool;
	
	private SearchApiConfig apiConfig;
	
    protected Socket getConnection(SearchApiConfigItem configItem) throws Exception {
        GenericObjectPool<Socket> pool;
        if (socketConnectionPool.containsKey(configItem)) {
            pool = socketConnectionPool.get(configItem);
        } else {
            pool = new GenericObjectPool<Socket>(new SocketConnectionFactory(configItem.getHost(), configItem.getPort()));
            pool.setTestOnBorrow(true);
            pool.setMaxActive(20);
            socketConnectionPool.put(configItem, pool);
        }

        return pool.borrowObject();
    }

    protected void returnConnection(SearchApiConfigItem configItem, Socket socket) throws Exception {
        GenericObjectPool<Socket> pool = socketConnectionPool.get(configItem);
        pool.returnObject(socket);
    }

	public ISearchApiResponse request(SearchApiConfig apiConfig, ISearchApiRequest request, int timeout_msec) throws ApiRemoteCallFailedException {
        SearchApiConfigItem configItem = apiConfig.getConfig(request.getService());
        if (configItem == null) {
            logger.error("搜索服务API配置不存在");
            return null;
        }

        logger.info("Request string: {}", request.toJSON().toString());

        String command = request.getCommand();

        String responseStr = null;
        // 获取请求响应
        Socket socket = null;
        try {
            socket = this.getConnection(configItem);

            if (timeout_msec > 0) {
                socket.setSoTimeout(timeout_msec);
            }

            // 发送请求命令
            OutputStream socketOut = socket.getOutputStream();
            socketOut.write(request.toJSON().toString().getBytes(CharsetConstant.CHARSET_UTF8));

            // 接收服务器的反馈
            InputStream socketIn = socket.getInputStream();
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(socketIn));
            responseStr = br.readLine();

            // 保持连接打开，取消超时时间设置
            if (timeout_msec > 0) {
                socket.setSoTimeout(0);
            }

            if (responseStr != null) {
                responseStr = CoreStringUtils.unicodeToString(responseStr);
            }
            logger.info("Response string: {}", responseStr);
        } catch (Exception e) {
            try {
                logger.error("request queryString is: {}", request.toJSON().toString());
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
            logger.error(e.getMessage(), e);
            throw new ApiRemoteCallFailedException("搜索服务API请求失败");
        } finally {
            if (socket != null) {
                logger.info("return connection, {}", socket);
                try {
                    this.returnConnection(configItem, socket);
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        if (StringUtils.isBlank(responseStr)) {
            logger.error("搜索服务请求响应为空, response={}", responseStr);
            throw new ApiRemoteCallFailedException("搜索服务请求响应为空");
        }

        JSONObject responseObject = null;
        try {
            responseObject = JSONObject.fromObject(responseStr);
        } catch (Exception e) {
            logger.error("将搜索服务请求响应转换成JSONObject出错", e);
        }

        // 实现调用socket连接请求并获得返回数据
        AbstractSearchApiResponse response = SearchApiResponseParserBinder.createInstance(command);
        if (response == null) {
            logger.error("根据command创建实例失败, command={}", command);
            throw new ApiRemoteCallFailedException("根据command创建实例失败");
        }

        response.setStatus(CoreJSONUtils.getInt(responseObject, SearchApiConstant.API_RETURN_STATUS));
        response.setSerial(CoreJSONUtils.getLong(responseObject, SearchApiConstant.API_RETURN_SERIAL));

        if (response.getStatus() != SearchApiConstant.RC_SUCCESS) {
            logger.error("搜索服务请求失败，status={}", response.getStatus());
            logger.error("request url is: {}:{}, parameter is: {}",
                    new Object[] {configItem.getHost(), configItem.getPort(), CoreStringUtils.unicodeToString(request.toJSON().toString())});
            logger.error("response content is: {}", responseStr);
        } else {
            // 之后成功时才进行body解析
            // 解析body
            JSON bodyJson = CoreJSONUtils.getJSON(responseObject, SearchApiConstant.API_RETURN_BODY);
            if (bodyJson != null) {
                ISearchApiResponseBodyParser parser = SearchApiResponseParserBinder.getParser(command);
                if (parser != null) {
                    try {
                        ISearchApiResponseBody body = parser.parse(bodyJson);
                        response.setBody(body);
                    } catch (Exception e) {
                        logger.error("解析body失败", e);
                        throw new ApiRemoteCallFailedException("解析body失败");
                    }
                }
            }
        }

		return response;
	}
	
	public ISearchApiResponse request(ISearchApiRequest request, int timeout_msec) throws ApiRemoteCallFailedException {
		return this.request(this.apiConfig, request, timeout_msec);
	}
	
	public ISearchApiResponse request(ISearchApiRequest request) throws ApiRemoteCallFailedException {
		int timeout = SearchApiConstant.API_REQUEST_TIME_OUT_DEFAULT;

		return request(request, timeout);
	}

	public IWarningTool getWarningTool() {
		return warningTool;
	}

	public void setWarningTool(IWarningTool warningTool) {
		this.warningTool = warningTool;
	}

    public SearchApiConfig getApiConfig() {
        return apiConfig;
    }

    public void setApiConfig(SearchApiConfig apiConfig) {
        this.apiConfig = apiConfig;
    }
}
